#pragma once

#include <QDebug>
#include <QString>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
#include <QIODevice>
#include <stdio.h>

#include "colors.h"

#define MAX_LOGFILE_SIZE (100 * 1024 * 1024)

namespace LogHandler {

enum class LogMode {
    Normal,
    Debug,
};
enum class LogFormat {
    NoFileInfo,
    WithFileInfo,
};

LogMode g_mode = LogMode::Normal;
LogFormat g_format = LogFormat::NoFileInfo;

QFile g_logfile;
bool g_savelog = false;

// enum QtMsgType { QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtInfoMsg, QtSystemMsg = QtCriticalMsg };
QString FormatColor(QtMsgType type)
{
    const char* output = nullptr;
    switch (type)
    {
    case QtDebugMsg:    output = C_CL; break;;
    case QtWarningMsg:  output = C_HY; break;;
    case QtCriticalMsg: output = C_HR; break;;
    case QtFatalMsg:    output = C_HR; break;;
    case QtInfoMsg:     output = C_HL; break;;
    }
    return QLatin1String(output);
}

QString FormatType(QtMsgType type, bool savefile = false)
{
    QString out;
    QString type_header = FormatColor(type);
    QString type_end = QLatin1String(C_CL);
    const char* type_str = nullptr;
    switch (type)
    {
    case QtDebugMsg:    type_str = "D"; break;
    case QtWarningMsg:  type_str = "W"; break;
    case QtCriticalMsg: type_str = "E"; break;
    case QtFatalMsg:    type_str = "F"; break;
    case QtInfoMsg:     type_str = "I"; break;
    }
    if (type_header.isEmpty()) {
        type_end.clear();
    }
    if (!savefile) {
        out = QString(QLatin1String("[%1%2%3]")).arg(type_header, QLatin1String(type_str), type_end);
    } else {
        out = QString(QLatin1String("[%1]")).arg(QLatin1String(type_str));
    }
    return out;
}

QString FormatMessage(QtMsgType type, const QString &message, bool savefile = false)
{
    QString out;
    QString message_header = FormatColor(type);
    QString message_end = QLatin1String(C_CL);
    if (message_header.isEmpty()) {
        message_end.clear();
    }
    if (!savefile) {
        out = QString(QLatin1String("%1%2%3")).arg(message_header, message, message_end);
    } else {
        out = QString(QLatin1String("%1")).arg(message);
    }
    return out;
}

#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
void MessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QDateTime datetime = QDateTime::currentDateTime();
    QString date = datetime.date().toString(QLatin1String("yyyy.MM.dd"));
    QString time = datetime.time().toString(QLatin1String("hh:mm:ss"));
    QString file = QLatin1String(context.file ? context.file : "??");
    QString line = QString::number(context.line);
    QString function = QLatin1String(context.function ? context.function : "????");
    QByteArray locale_message = msg.toUtf8();

    // [FLAG] DATE TIME - MSG
    QString prefix = QString(QLatin1String("%1 %2")).arg(date, time);
    QString prefix_log = QString(QLatin1String("%1 %2")).arg(date, time);
    // [FLAG] DATE TIME [FILE:LINE, FUNCTION] - MSG
    if (g_format == LogFormat::WithFileInfo) {
        QString finfo;
        QString finfo_log;
        if (!file.isEmpty() && !line.isEmpty()) {
            if (function.isEmpty()) {
                finfo = finfo.append(QString(QLatin1String("[%1%2%3:%4]")).arg(QLatin1String(C_LB), file, QLatin1String(C_CL), line));
                finfo_log = finfo_log.append(QString(QLatin1String("[%1:%2]")).arg(file, line));
            } else {
                finfo = finfo.append(QString(QLatin1String("[%1%2%3:%4 - %5%6%7]")).arg(QLatin1String(C_LB), file, QLatin1String(C_CL), line, QLatin1String(C_LY), function, QLatin1String(C_CL)));
                finfo_log = finfo_log.append(QString(QLatin1String("[%1:%2 - %3]")).arg(file, line, function));
            }
        }
        if (!finfo.isEmpty()) {
            prefix = prefix.append(QString(QLatin1String(" %1")).arg(finfo));
        }
        if (!finfo_log.isEmpty()) {
            prefix_log = prefix_log.append(QString(QLatin1String(" %1")).arg(finfo_log));
        }
    }

    QString message = QLatin1String(locale_message);
    QString p_message = QLatin1String("");
    if (locale_message.startsWith(CTL) && locale_message.contains("[")) {
        int index = message.indexOf(QLatin1String("]" C_CL)) + QString(QLatin1String("]" C_CL)).length();
        p_message = message.left(index);
        if (p_message.contains(QLatin1String(" - "))) {
            message = QLatin1String(locale_message.mid(index).trimmed());
        }
    }

    FILE *g_channel = stderr;
    if (type == QtInfoMsg && g_mode != LogMode::Debug) {
        g_channel = stdout;
    }

    QString p_type = FormatType(type);
    QString p_type_log = FormatType(type, true);
    QString s_message = FormatMessage(type, message);
    QString s_message_log = FormatMessage(type, message, true);
    QString output;
    QString output_log;
    if (!s_message.isEmpty()) {
        if (p_message.isEmpty()) {
            output.append(QString(QLatin1String("%1 %2 - %3")).arg(p_type, prefix, s_message));
        } else {
            output.append(QString(QLatin1String("%1 %2 - %3 %4")).arg(p_type, prefix, p_message, s_message));
        }
        fprintf(g_channel, "%s\n", output.toLocal8Bit().constData());
    }
    if (!s_message_log.isEmpty()) {
        output_log.append(QString(QLatin1String("%1 %2 - %3")).arg(p_type_log, prefix_log, s_message_log));
        if (g_logfile.isOpen()) {
            g_logfile.write(output_log.toLocal8Bit() + "\n");
        }
    }
}
#endif // QT_VERSION_CHECK

// 在 main 函数中开头位置调用
void InstallLogHandler(const QString &logfile = QString(), bool overwrite = true, LogMode mode = LogMode::Normal, LogFormat format = LogFormat::NoFileInfo)
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    qInstallMessageHandler(MessageHandler);
    g_savelog = !logfile.isEmpty();
    g_logfile.setFileName(logfile);
    g_mode = mode;
    g_format = format;

    if (g_savelog) {
        QFileInfo fi(logfile);
        QIODevice::OpenMode mode = QIODevice::Text | QIODevice::Unbuffered | QIODevice::WriteOnly;
        if ((fi.size() < MAX_LOGFILE_SIZE) && !overwrite) {
            mode |= QIODevice::Append;
        } else {
            mode |= QIODevice::Truncate;
        }
        g_logfile.open(mode);
    }
#endif
}

QString GetLogFile()
{
    return g_logfile.fileName();
}

// 程序退出前调用，关闭日志文件
void CloseLogHandler()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    if (g_savelog) {
        g_logfile.flush();
        g_logfile.close();
    }
#endif
}

void SetLogMode(LogMode mode)
{
    g_mode = mode;
}

void SetLogFormat(LogFormat format)
{
    g_format = format;
}

} // namespace LogHandler
